package com.practice.pagehelper.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.client.StandardHttpRequestRetryHandler;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.nio.charset.StandardCharsets;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * @Description :
 * @Author : guxuemin
 * @Version : V1.0.0
 * @Date : 2022/8/25 21:44
 */
@Configuration
public class RestConfig {

    /**
     * 连接超时时间
     */
    private static final int SOCKET_TIME_OUT = 10000;

    /**
     * 请求超时时间
     */
    private static final int REQUEST_TIME_OUT = SOCKET_TIME_OUT / 2;

    /**
     * http单路由连接池
     */
    @Value("${http.route.poolsize:30}")
    private int routePoolSize;

    /**
     * http 请求模板
     *
     * @return http 请求模板
     * @throws NoSuchAlgorithmException 无加密算法
     * @throws KeyStoreException        ssl密钥异常
     * @throws KeyManagementException   ssl服务异常
     */
    @Bean
    public RestTemplate restTemplate() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
        RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient()));

        HttpMessageConverter<?> converter = new StringHttpMessageConverter(StandardCharsets.UTF_8);
        List<HttpMessageConverter<?>> convertList = new ArrayList<>();
        convertList.add(converter);

        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        List<MediaType> asList = Arrays.asList(MediaType.APPLICATION_JSON, new MediaType("application", "*+json"),
                new MediaType("text", "javascript"));
        jackson2HttpMessageConverter.setSupportedMediaTypes(asList);
        FormHttpMessageConverter formHttpMessageConverter = new FormHttpMessageConverter();
        formHttpMessageConverter.setSupportedMediaTypes(Collections.singletonList(MediaType.MULTIPART_FORM_DATA));
        convertList.add(jackson2HttpMessageConverter);
        convertList.add(formHttpMessageConverter);
        template.setMessageConverters(convertList);

        return template;
    }

    /**
     * 支持string的json解析器
     *
     * @return
     */
    @Bean
    public HttpMessageConverter objectHttpMessageConverter(Jackson2ObjectMapperBuilder builder) {
        ObjectMapper mapper = builder.build();
        MappingJackson2HttpMessageConverter httpMessageConverter = new MappingJackson2HttpMessageConverter(mapper);
        httpMessageConverter.setDefaultCharset(StandardCharsets.UTF_8);
        ArrayList<MediaType> mediaTypes = Lists.newArrayList(MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8,
                MediaType.TEXT_PLAIN);
        httpMessageConverter.setSupportedMediaTypes(mediaTypes);
        return httpMessageConverter;
    }

    /**
     * 构建httpClient
     *
     * @return HttpClient
     * @throws KeyStoreException        KeyStoreException
     * @throws NoSuchAlgorithmException NoSuchAlgorithmException
     * @throws KeyManagementException   KeyManagementException
     */
    private HttpClient httpClient() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        SSLContext sslcontext =
                SSLContexts.custom().loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();
        SSLConnectionSocketFactory sslConnectionSocketFactory = new SSLConnectionSocketFactory(
                sslcontext, new String[] { "TLSv1.2" }, null, NoopHostnameVerifier.INSTANCE);

        Registry<ConnectionSocketFactory> socketFactoryRegistry;
        socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                .register("http", PlainConnectionSocketFactory.getSocketFactory())
                .register("https", sslConnectionSocketFactory).build();

        // 5秒超时
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(REQUEST_TIME_OUT)
                .setSocketTimeout(SOCKET_TIME_OUT).setConnectTimeout(SOCKET_TIME_OUT).build();
        SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(REQUEST_TIME_OUT).build();
        PoolingHttpClientConnectionManager cm =
                new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        cm.setMaxTotal(routePoolSize);
        // 单路由最大并发数
        cm.setDefaultMaxPerRoute(routePoolSize);
        return HttpClients.custom()
                .setDefaultRequestConfig(requestConfig)
                .setRetryHandler(new StandardHttpRequestRetryHandler())
                .setDefaultSocketConfig(socketConfig).setConnectionManager(cm).build();
    }
}
